# = Authorize.net Customer Information Management
#
# == General Usage
#   gateway = ActiveMerchant::Billing::AuthorizeNetCimGateway.new :login => 'name', :password => 'transactionKey'
#   
#   resp = gateway.create_profile(profile_data)
#   if resp.success?
#     customer.profile_id = resp.params['data'][:customerProfileId]
#   else
#     puts resp.message
#   end
#
# Every successful #create_ request returns the id of the newly created record.
# This id is used in all subsequent requests
#
#   resp = gateway.get_profile(customer.profile_id)   
#   if resp.success?
#     profile_data = resp.params['data']  # profile_data now contains a CustomerProfile object
#   else
#     puts resp.message
#   end
#
# == Data Objects
# Data used in the API is represented as a series of nested objects that behave similarly to ActiveRecord objects.
#  
#
#
#
module ActiveMerchant
  module Billing
    class AuthorizeNetCimGateway < Gateway
      class_inheritable_accessor :test_url, :live_url
    
      self.test_url = 'https://apitest.authorize.net/xml/v1/request.api'
      self.live_url = 'https://api.authorize.net/xml/v1/request.api'
  
      self.supported_countries = ['US']
      self.supported_cardtypes = [:visa, :master, :american_express, :discover]
      self.homepage_url = 'http://www.authorize.net/'
      self.display_name = 'Authorize.net'
  
      include ActiveMerchant::Billing::AuthorizeNetCimAPI
  
      attr_accessor :login, :password, :url
  
      def initialize(options={})
        requires!(options, :login, :password)
        @options = options
        @login = options[:login]
        
        @password = options[:password]
        @url = self.test_url
        super
      end
      
      def authorize(money, profile_id, payment_profile_id, transaction_data={}, address_id = nil, ref_id = nil)
        transaction(money, profile_id, payment_profile_id, transaction_data, 'AuthOnly', address_id, ref_id)
      end      
      
      def purchase(money, profile_id, payment_profile_id, transaction_data={}, address_id = nil, ref_id = nil)
        transaction(money, profile_id, payment_profile_id, transaction_data, 'AuthCapture', address_id, ref_id)
      end
      
      def capture(money, profile_id, payment_profile_id, transaction_data={}, address_id = nil, ref_id = nil)
        transaction(money, profile_id, payment_profile_id, transaction_data, 'CaptureOnly', address_id, ref_id)        
      end
      
      def create_profile(customer_profile, ref_id=nil)
        CreateCustomerProfile.new(:profile => customer_profile, :refId => ref_id).submit(self)
      end
      
      def create_payment_profile(profile_id, payment_profile, ref_id=nil)
        CreateCustomerPaymentProfile.new(:customerProfileId => profile_id, 
                                         :payment_profile => payment_profile, :refId => ref_id).submit(self)
      end
  
      def create_shipping_address(profile_id, address, ref_id=nil)
        CreateCustomerShippingAddress.new(:customerProfileId => profile_id, :address => address,
                                          :refId => ref_id).submit(self)
      end
      
      def get_profile(profile_id)
        GetCustomerProfile.new(:customerProfileId => profile_id).submit(self)
      end
      
      def get_payment_profile(profile_id, payment_profile_id)
        GetCustomerPaymentProfile.new(:customerProfileId => profile_id, 
                                      :customerPaymentProfileId => payment_profile_id).submit(self)
      end
      
      def get_shipping_address(profile_id, address_id)
        GetCustomerShippingAddress.new(:customerProfileId => profile_id, 
                                       :customerAddressId => address_id).submit(self)        
      end
      
      def update_profile(profile_id, customer_profile, ref_id = nil)
        UpdateCustomerProfile.new(:profile => customer_profile.merge(:customerProfileId => profile_id),
                                  :refId => ref_id).submit(self)
      end
  
      def update_payment_profile(profile_id, payment_profile, ref_id = nil)
        UpdateCustomerPaymentProfile.new(:customerProfileId => profile_id, :paymentProfile => payment_profile,
                                         :refId => ref_id).submit(self)
      end
      
      def update_shipping_address(profile_id, address, ref_id = nil)
        UpdateCustomerShippingAddress.new(:customerProfileId => profile_id, :address => address, :refId => nil).submit(self)
      end
      
      def delete_profile(profile_id, ref_id = nil)
        DeleteCustomerProfile.new(:customerProfileId => profile_id, :refId => ref_id).submit(self)
      end
      
      def delete_payment_profile(profile_id, payment_profile_id, ref_id = nil)
        DeleteCustomerPaymentProfile.new(:customerProfileId => profile_id,
                                        :customerPaymentProfileId => payment_profile_id, :refId => ref_id).submit(self)
      end
      
      def delete_shipping_address(profile_id, address_id, ref_id = nil)
        DeleteCustomerShippingAddress.new(:customerProfileId => profile_id, :customerAddressId => address_id,
                                          :refId => nil).submit(self)
      end
      
      def validate_payment_profile(profile_id, payment_profile_id, address_id = nil, validation_mode = :testMode)
        ValidateCustomerPaymentProfile.new(:customerProfileId  => profile_id,
                                           :customerPaymentProfileId => payment_profile_id,
                                           :customerAddressId => address_id,
                                           :validationMode => validation_mode).submit(self)
      end
      
      def test?
        @options[:test] || Base.gateway_mode == :test
      end

      def authentication
        Authentication.new :name => @login, :transactionKey => @password
      end

    private
    
      def transaction(money, profile_id, payment_profile_id, transaction_data, type, address_id = nil, ref_id = nil)
        res = CreateCustomerProfileTransaction.new( :transaction => {
                                                     :type => type,
                                                     :amount => money, :customerProfileId => profile_id, 
                                                     :customerPaymentProfileId => payment_profile_id, 
                                                     :customerShippingAddressId => address_id
                                                     }.merge(transaction_data)).submit(self)
        puts res.inspect
        # Transaction requests are very similar to those of the standard AuthorizeNet Gateway
        # so we'll use their code to finish the processing.
        # This should will be refactored in the patch to make this part a simple method call on the AuthorizeNet Gateway
        if res.success?
          direct_response = res.params['data']['directResponse']
          direct_response = ".#{direct_response.gsub(/,/,'$,$')}.." # AuthorizeNet uses response[1..-2].split(/\$,\$/)
          puts direct_response
          gateway = ActiveMerchant::Billing::AuthorizeNetGateway.new @options
          gateway.instance_eval %Q{
          
          @response = parse('#{direct_response}')

          success = @response[:response_code] == APPROVED
          message = message_from(@response)

          # Return the response. The authorization can be taken out of the transaction_id 
          # Test Mode on/off is something we have to parse from the response text.
          # It usually looks something like this
          #
          #   (TESTMODE) Successful Sale
          test_mode = test? || message =~ /TESTMODE/

          Response.new(success, message, @response, 
            :test => test_mode, 
            :authorization => @response[:transaction_id]
          )        
          }
        end
      end
    end
  end
end